package cn.heygod.nacostool.client;

import cn.heygod.nacostool.config.NacosConfig;
import cn.heygod.nacostool.config.DeployConfig;
import cn.heygod.nacostool.util.StrUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
import com.alibaba.nacos.client.naming.NacosNamingService;
import com.alibaba.nacos.client.naming.net.NamingProxy;
import com.alibaba.nacos.client.naming.utils.UtilAndComs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * @author Simon
 */
@Component
public class NacosClient {

    private Logger logger = LoggerFactory.getLogger(getClass());

    public static final int DEFAULT_PAGE_NO = 0;
    public static final int DEFAULT_PAGE_size = 100;

    @Autowired
    private NacosConfig nacosConfig;

    @Autowired
    private DeployConfig deployConfig;

    private NamingService namingService = null;

    protected NamingService getNamingService(){
        try {
            if(namingService == null){
                namingService = NacosFactory.createNamingService(nacosConfig.createProps());
            }

            return  namingService;
        } catch (NacosException e) {
            throw new RuntimeException(e);
        }
    }


    public List<Instance> getInstances(String serviceName){
        return getInstances(serviceName,nacosConfig.getGroup());
    }

    public List<Instance> getInstances(String serviceName, String groupName){
        try {
            return getNamingService().getAllInstances(serviceName,groupName);
        } catch (NacosException e) {
            throw new RuntimeException(e);
        }
    }

    public List<String> getServicesFromServer(int pageNo, int pageSize,String group){
        try {
            ListView<String> listView = getNamingService().getServicesOfServer(pageNo,pageSize,group);

            return listView.getData();
        } catch (NacosException e) {
            throw new RuntimeException(e);
        }
    }

    public List<String> getServicesFromServer(){
        return getServicesFromServer(DEFAULT_PAGE_NO,DEFAULT_PAGE_size, nacosConfig.getGroup());
    }


    public Map<String,List<Instance>> getAllInstancesMapFromServer(){
        Map<String,List<Instance>> instancesMap = new HashMap<>();
        List<String> services = getServicesFromServer();
        for (String serviceName:  services  ) {
            instancesMap.put(serviceName, getInstances(serviceName,nacosConfig.getGroup()));
        }
        return instancesMap;
    }


    /**
     * 节点下线
     * @param serviceName
     * @param ip
     * @param port
     * @throws NacosException
     */
    public void deregisterInstance(String serviceName, String ip, int port)  {
        try {
            NamingService namingService = getNamingService();
            String group = nacosConfig.getGroup();
            Instance instance = new Instance();
            instance.setIp(ip);
            instance.setPort(port);
            instance.setWeight(2.0);
            instance.setClusterName(nacosConfig.getClusterName());
            instance.setEphemeral(false);
            instance.setEnabled(true);
            instance.setMetadata(MapUtil.of("preserved.register.source","SPRING_CLOUD"));

            namingService.deregisterInstance(serviceName,group,instance);
//            instance.setEphemeral(false);
           // namingService.registerInstance(serviceName,group,instance);
//            namingService.deregisterInstance(serviceName,instance);
            //getNamingService().deregisterInstance(serviceName, nacosConfig.getGroup(), ip, port, nacosConfig.getClusterName());
        } catch (NacosException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 节点上线
     * @param serviceName
     * @param ip
     * @param port
     * @throws NacosException
     */
    public void registerInstance(String serviceName, String ip, int port)  {
        try {
            getNamingService().registerInstance(serviceName, nacosConfig.getGroup(), ip, port, nacosConfig.getClusterName());
        } catch (NacosException e) {
            throw new RuntimeException(e);
        }
    }


    private NamingProxy getNamingProxy(NacosNamingService namingService){

        return (NamingProxy)ReflectUtil.getFieldValue(namingService,"serverProxy");
    }

    public void offline(String instanceId ){
        updateInstance(instanceId,false);
    }


    public void online(String instanceId){
        updateInstance(instanceId,true);
    }

    private void updateInstance(String instanceId,boolean enable){
        NamingProxy proxy = getNamingProxy((NacosNamingService)getNamingService());
        Instance instance = findInstance(instanceId);
        instance.setEnabled(enable);
        try {
            proxy.updateInstance(instance.getServiceName(), nacosConfig.getGroup(), instance);
        } catch (NacosException e) {
            throw new RuntimeException(e);
        }
    }


    public Instance findInstance(String instanceId){
        return findInstance(StrUtil.parseServiceNameById(instanceId),instanceId);
    }

    public Instance findInstance(String serviceName,String instanceId){
        List<Instance> list = getAllInstances(serviceName);
        for (Instance instance:
             list) {
            if(instance.getInstanceId().equals(instanceId)){
                return  instance;
            }
        }
        return null;
    }

    public List<Instance> getAllInstances(String serviceName){
        String json = getAllInstancesJson(serviceName );
        JSONObject jsonObject = JSONUtil.parseObj(json);
        return jsonObject.getBeanList("list",Instance.class);
    }
    /**
     * 包含已下线的节点
     * http://0.0.0.0:8848/nacos/v1/ns/catalog/instances?&accessToken=eyJhbGciOiJIUzI1NiJ9..
     * &serviceName=nacos-service
     * &clusterName=DEFAULT
     * &groupName=demo_group
     * &pageSize=10&pageNo=1&namespaceId=
     * @param serviceName
     */
    public String getAllInstancesJson(String serviceName){
        NamingProxy proxy = getNamingProxy((NacosNamingService)getNamingService());
        try {

            Map<String, String> params = new HashMap(8);
            params.put("serviceName", serviceName);
            params.put("clusterName", nacosConfig.getClusterName());
            params.put("groupName", nacosConfig.getGroup());
            params.put("namespaceId", nacosConfig.getNamespace());

            params.put("pageSize", "20");
            params.put("pageNo", "1");
            return proxy.reqApi(UtilAndComs.nacosUrlBase + "/catalog/instances", params, "GET");

        } catch (NacosException e) {
            if(e.getErrMsg().indexOf("is not found") != -1){
                logger.warn("failed to req instances,return empty list.remark:{}",e.getErrMsg());
                return "{'list':[]}";
            }else{
                throw new RuntimeException(e);
            }

        }

    }


    public List<Instance> getInstancesAllServices(){
        List instances = new ArrayList();
        List<String> servicesFromServ = getServicesFromServer();
        //取交集
        servicesFromServ.retainAll(deployConfig.getServicesList());

        for (String serviceName:  servicesFromServ  ) {
            List<Instance> nodes = getAllInstances(serviceName);
            instances.addAll(nodes);

        }
        return instances;
    }
}
